package com.wyz.tracing.infrastructure.repository;

import com.wyz.tracing.domain.entity.base.Aggregate;
import com.wyz.tracing.domain.entity.base.Identifier;
import com.wyz.tracing.domain.entity.tracing.different.AggregateDifference;
import com.wyz.tracing.domain.service.tracing.AggregateTracingManager;
import com.wyz.tracing.domain.service.tracing.ThreadLocalTracingManager;
import com.wyz.tracing.enums.DifferenceType;

import java.io.IOException;

/**
 * 仓储支持
 * <p>
 * 追踪聚合根下实体的变化
 *
 * @author wyz
 * @version 1.0
 * @date 2023/4/18
 */
public abstract class RepositorySupport<T extends Aggregate<ID>, ID extends Identifier> implements Repository<T, ID> {

    /**
     * 查询
     * 实现通过DAO查询数据，并转换为实体对象
     * 由{@link Repository}调用
     * @param id
     * @return
     */
    protected T onSelect(ID id) {
        throw new UnsupportedOperationException();
    }

    /**
     * 新增
     * 实现通过DAO插入数据
     * {@link Repository#remove(Aggregate)} )}
     * @param aggregate
     */
    protected void onInsert(T aggregate) {
        throw new UnsupportedOperationException();
    }


    protected void onUpdate(T aggregate, AggregateDifference<T, ID> aggregateDifference) {
        throw new UnsupportedOperationException();
    }

    protected void onDelete(T aggregate) {
        throw new UnsupportedOperationException();
    }

    protected void attach(T aggregate) throws IOException, ClassNotFoundException {
        this.aggregateTracingManager.attach(aggregate);
    }

    protected void detach(T aggregate) {
        this.aggregateTracingManager.detach(aggregate);
    }


    private final AggregateTracingManager<T, ID> aggregateTracingManager;

    /**
     *
     * @param aggregate
     * @return
     * @throws IllegalAccessException
     */
    protected AggregateDifference<T, ID> different(T aggregate) throws IllegalAccessException {
        return this.aggregateTracingManager.different(aggregate);
    }


    public RepositorySupport() {
        this.aggregateTracingManager = new ThreadLocalTracingManager<>();
    }

    @Override
    public T find(ID identifier) throws IOException, ClassNotFoundException {
        T aggregate = this.onSelect(identifier);
        if (aggregate != null) {
            this.aggregateTracingManager.attach(aggregate);
        }
        return aggregate;
    }

    @Override
    public void save(T aggregate) throws IllegalAccessException, IOException, ClassNotFoundException {
        AggregateDifference<T, ID> aggregateDifference = this.aggregateTracingManager.different(aggregate);
        if (DifferenceType.ADDED.equals(aggregateDifference.getDifferentType())) {
            this.onInsert(aggregate);
        } else {
            this.onUpdate(aggregate, aggregateDifference);
        }
        this.aggregateTracingManager.merge(aggregate);
    }


    @Override
    public void remove(T aggregate) {
        this.onDelete(aggregate);
        this.aggregateTracingManager.detach(aggregate);
    }
}
